/******************************************************************************
 * $Id: shp_vsi.c 33714 2016-03-13 05:42:13Z goatbar $
 *
 * Project:  OpenGIS Simple Features Reference Implementation
 * Purpose:  IO Redirection via VSI services for shp/dbf io.
 * Author:   Frank Warmerdam, warmerdam@pobox.com
 *
 ******************************************************************************
 * Copyright (c) 2007,  Frank Warmerdam
 * Copyright (c) 2009-2013, Even Rouault <even dot rouault at mines-paris dot org>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included
 * in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 ****************************************************************************/

#include "shp_vsi.h"
#include "cpl_error.h"
#include "cpl_conv.h"



CPL_CVSID("$Id: shp_vsi.c 33714 2016-03-13 05:42:13Z goatbar $");

//typedef struct
//{
//    VSILFILE *fp;
//    char     *pszFilename;
//    int       bEnforce2GBLimit;
//    int       bHasWarned2GB;
//    SAOffset  nCurOffset;
//} OGRSHPDBFFile;



/************************************************************************/
/*                         VSI_SHP_GetVSIL()                            */
/************************************************************************/

VSILFILE *VSI_SHP_GetVSIL(SAFile file)
{
    return NULL;
    /*OGRMongoSHPDBFFile* pFile = (OGRMongoSHPDBFFile*) file;
	return pFile;*/
}

// end?
int VSI_Eof(SAFile th)
{
    OGRMongoSHPDBFFile *pFile = (OGRMongoSHPDBFFile *)th;
    GridFile file = pFile->grid->findFileByName(pFile->fileName);
    return pFile->nExpectedPos == file.getContentLength();
}

/************************************************************************/
/*                        VSI_SHP_GetFilename()                         */
/************************************************************************/

const char *VSI_SHP_GetFilename(SAFile file)
{
    OGRMongoSHPDBFFile *pFile = (OGRMongoSHPDBFFile *)file;
    return pFile->fileName;
}

/************************************************************************/
/*                         VSI_SHP_OpenInternal()                       */
/************************************************************************/

static SAFile VSI_SHP_OpenInternal(const char *pszFilename, const char *pszAccess,
				   int bEnforce2GBLimit)

{
    cout << pszFilename << endl;

    string connStr(pszFilename);
    vector<string> conns;
    boost::split(conns, connStr, boost::is_any_of("$"));
    cout << "0" << endl;
    if (conns.size() != 6 || conns[0] != "mongoshp") // params invalide
    {
		cout << conns[0] << endl;
		cout << conns[1] << endl;
		cout << conns[2] << endl;
		cout << conns[3] << endl;
		cout << conns[4] << endl;
		cout << conns[5] << endl;
		cout << conns[6] << endl;
	return NULL;
    }
    cout << "1" << endl;
    //  conn mongo
    mongo::client::initialize();
    mongo::DBClientConnection *mongoConn = new mongo::DBClientConnection();
    try
    {
	cout << "2" << endl;
	mongoConn->connect(conns[1]);
    }
    catch (const std::exception &)
    {
	return NULL;
    }
    cout << "3" << endl;
    string errmsg = "auth success";
    bool auth = mongoConn->auth(conns[2], conns[3], conns[4], errmsg);
    if (!auth)
    {
	return NULL;
    }
    cout << "4" << endl;
    GridFS *grid = new GridFS(*mongoConn, conns[2]);
    GridFile gfile = grid->findFileByName(conns[5]);
    if (!gfile.exists())
    {
	return NULL;
    }
    cout << "5" << endl;
    OGRMongoSHPDBFFile *psGMTH = static_cast<OGRMongoSHPDBFFile *>(CPLMalloc(sizeof(OGRMongoSHPDBFFile)));

    psGMTH->grid = grid;
    psGMTH->conn = mongoConn;
    strcpy(psGMTH->fileName, conns[5].c_str());
    psGMTH->nExpectedPos = 0;
    psGMTH->bAtEndOfFile = FALSE;
    psGMTH->bEnforce2GBLimit = bEnforce2GBLimit;

    cout << "6" << endl;

    return (SAFile)psGMTH;
}

/************************************************************************/
/*                            VSI_SHP_Open()                            */
/************************************************************************/

static SAFile VSI_SHP_Open(const char *pszFilename, const char *pszAccess)

{
    return VSI_SHP_OpenInternal(pszFilename, pszAccess, FALSE);
}

/************************************************************************/
/*                        VSI_SHP_Open2GBLimit()                        */
/************************************************************************/

static SAFile VSI_SHP_Open2GBLimit(const char *pszFilename, const char *pszAccess)

{
    return VSI_SHP_OpenInternal(pszFilename, pszAccess, TRUE);
}

/************************************************************************/
/*                            VSI_SHP_Read()                            */
/************************************************************************/

static SAOffset VSI_SHP_Read(void *p, SAOffset size, SAOffset nmemb, SAFile file)

{

    //OGRSHPDBFFile* pFile = (OGRSHPDBFFile*) file;
    //SAOffset ret = (SAOffset) VSIFReadL( p, (size_t) size, (size_t) nmemb,
    //                             pFile->fp );
    //pFile->nCurOffset += ret * size;
    //return ret;

    OGRMongoSHPDBFFile *psGMTH = (OGRMongoSHPDBFFile *)file;
    if (psGMTH && psGMTH->grid)
    {
	GridFile file = psGMTH->grid->findFileByName(psGMTH->fileName);
	int chunkSize = file.getChunkSize();
	int chunkIndex = floor((double)psGMTH->nExpectedPos / (double)chunkSize);
	int chunkSpan = psGMTH->nExpectedPos % chunkSize;
	unsigned long byteSize = size * nmemb;
	unsigned long bufSpan = 0;
	char *startPos = (char *)p;
	while (byteSize > 0)
	{
	    GridFSChunk chunk = file.getChunk(chunkIndex);
	    int len;
	    const char *data = chunk.data(len);
	    memcpy(startPos + bufSpan, data + chunkSpan, min(byteSize, len - chunkSpan));
	    bufSpan += min(byteSize, len - chunkSpan);
	    byteSize -= min(byteSize, len - chunkSpan);
	    chunkSpan = 0;
	    chunkIndex++;
	}

	psGMTH->nExpectedPos += bufSpan;

	return bufSpan / size;
    }
    return 0;
}

/************************************************************************/
/*                      VSI_SHP_WriteMoreDataOK()                       */
/************************************************************************/

int VSI_SHP_WriteMoreDataOK(SAFile file, SAOffset nExtraBytes)
{
    // can not modify the file now;
    // liumurong
    return TRUE;

    //OGRSHPDBFFile* pFile = (OGRSHPDBFFile*) file;
    //if( pFile->nCurOffset + nExtraBytes > 0x7FFFFFFF )
    //{
    //    if( pFile->bEnforce2GBLimit )
    //    {
    //        CPLError( CE_Failure, CPLE_AppDefined, "2GB file size limit reached for %s",
    //                  pFile->pszFilename );
    //        return FALSE;
    //    }
    //    else if( !pFile->bHasWarned2GB )
    //    {
    //        pFile->bHasWarned2GB = TRUE;
    //        CPLError( CE_Warning, CPLE_AppDefined, "2GB file size limit reached for %s. "
    //                  "Going on, but might cause compatibility issues with third party software",
    //                  pFile->pszFilename );
    //    }
    //}

    //return TRUE;
}

/************************************************************************/
/*                           VSI_SHP_Write()                            */
/************************************************************************/

static SAOffset VSI_SHP_Write(void *p, SAOffset size, SAOffset nmemb, SAFile file)

{
    // file can not write now ;
    // liumurong;
    return 0;

    //OGRSHPDBFFile* pFile = (OGRSHPDBFFile*) file;
    //SAOffset ret;
    //if( !VSI_SHP_WriteMoreDataOK( file, size * nmemb ) )
    //    return 0;
    //ret = (SAOffset) VSIFWriteL( p, (size_t) size, (size_t) nmemb,
    //                              pFile->fp );
    //pFile->nCurOffset += ret * size;
    //return ret;
}

/************************************************************************/
/*                            VSI_SHP_Seek()                            */
/************************************************************************/

static SAOffset VSI_SHP_Seek(SAFile file, SAOffset offset, int whence)

{

    //OGRSHPDBFFile* pFile = (OGRSHPDBFFile*) file;
    //SAOffset ret = (SAOffset) VSIFSeekL( pFile->fp, (vsi_l_offset) offset, whence );
    //if( whence == 0 && ret == 0)
    //    pFile->nCurOffset = offset;
    //else
    //    pFile->nCurOffset = (SAOffset) VSIFTellL( pFile->fp );
    //return ret;

    //

    OGRMongoSHPDBFFile *psGMTH = reinterpret_cast<OGRMongoSHPDBFFile *>(file);

    ///
    if (whence == SEEK_END)
    {
	if (psGMTH->bAtEndOfFile)
	{
	    return static_cast<SAOffset>(psGMTH->nExpectedPos);
	}
	psGMTH->bAtEndOfFile = TRUE;
	GridFile file = psGMTH->grid->findFileByName(psGMTH->fileName);
	psGMTH->nExpectedPos = file.getContentLength();
	return psGMTH->nExpectedPos;
    }

    psGMTH->bAtEndOfFile = FALSE;
    psGMTH->nExpectedPos = offset;

    return 0;
    //return psGMTH->nExpectedPos;
}

/************************************************************************/
/*                            VSI_SHP_Tell()                            */
/************************************************************************/

static SAOffset VSI_SHP_Tell(SAFile file)

{
    //OGRSHPDBFFile* pFile = (OGRSHPDBFFile*) file;
    //return (SAOffset) pFile->nCurOffset;

    OGRMongoSHPDBFFile *psGMTH = reinterpret_cast<OGRMongoSHPDBFFile *>(file);
    return psGMTH->nExpectedPos;
}

/************************************************************************/
/*                           VSI_SHP_Flush()                            */
/************************************************************************/

static int VSI_SHP_Flush(SAFile file)

{

    // shp file can not flush now
    // liumurong
    return 0;

    /*OGRSHPDBFFile* pFile = (OGRSHPDBFFile*) file;
    return VSIFFlushL( pFile->fp );*/
}

/************************************************************************/
/*                           VSI_SHP_Close()                            */
/************************************************************************/

static int VSI_SHP_Close(SAFile file)

{
    //OGRSHPDBFFile* pFile = (OGRSHPDBFFile*) file;
    //int ret = VSIFCloseL( pFile->fp );
    //CPLFree(pFile->pszFilename);
    //CPLFree(pFile);
    //return ret;

    OGRMongoSHPDBFFile *psGMTH = reinterpret_cast<OGRMongoSHPDBFFile *>(file);

    CPLFree(psGMTH->grid);
    CPLFree(psGMTH->conn);
    CPLFree(psGMTH);
    return 0;
}

/************************************************************************/
/*                              SADError()                              */
/************************************************************************/

static void VSI_SHP_Error(const char *message)

{
    CPLError(CE_Failure, CPLE_AppDefined, "%s", message);
}

/************************************************************************/
/*                           VSI_SHP_Remove()                           */
/************************************************************************/

static int VSI_SHP_Remove(const char *pszFilename)

{
    // can not remove file now
    return 0;

    //return VSIUnlink( pszFilename );
}

/************************************************************************/
/*                        SASetupDefaultHooks()                         */
/************************************************************************/

void SASetupDefaultHooks(SAHooks *psHooks)

{
    psHooks->FOpen = VSI_SHP_Open;
    psHooks->FRead = VSI_SHP_Read;
    psHooks->FWrite = VSI_SHP_Write;
    psHooks->FSeek = VSI_SHP_Seek;
    psHooks->FTell = VSI_SHP_Tell;
    psHooks->FFlush = VSI_SHP_Flush;
    psHooks->FClose = VSI_SHP_Close;
    psHooks->Remove = VSI_SHP_Remove;

    psHooks->Error = VSI_SHP_Error;
    psHooks->Atof = CPLAtof;
}

/************************************************************************/
/*                         VSI_SHP_GetHook()                            */
/************************************************************************/

static const SAHooks sOGRHook =
    {
	VSI_SHP_Open,
	VSI_SHP_Read,
	VSI_SHP_Write,
	VSI_SHP_Seek,
	VSI_SHP_Tell,
	VSI_SHP_Flush,
	VSI_SHP_Close,
	VSI_SHP_Remove,
	VSI_SHP_Error,
	CPLAtof};

static const SAHooks sOGRHook2GBLimit =
    {
	VSI_SHP_Open2GBLimit,
	VSI_SHP_Read,
	VSI_SHP_Write,
	VSI_SHP_Seek,
	VSI_SHP_Tell,
	VSI_SHP_Flush,
	VSI_SHP_Close,
	VSI_SHP_Remove,
	VSI_SHP_Error,
	CPLAtof};

const SAHooks *VSI_SHP_GetHook(int b2GBLimit)
{
    return (b2GBLimit) ? &sOGRHook2GBLimit : &sOGRHook;
}
